/*
* Sonatype Application Build Lifecycle
* Copyright (C) 2009 Sonatype, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/
package com.redhat.tools.nexus.maven.plugin.descriptor;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.model.License;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
import org.apache.maven.scm.manager.ScmManager;
import org.apache.maven.scm.provider.ScmProviderRepository;
import org.apache.maven.scm.provider.svn.AbstractSvnScmProvider;
import org.apache.maven.scm.provider.svn.command.info.SvnInfoItem;
import org.apache.maven.scm.provider.svn.command.info.SvnInfoScmResult;
import org.apache.maven.scm.repository.ScmRepository;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.util.StringUtils;
import org.sonatype.plugin.ExtensionPoint;
import org.sonatype.plugin.Managed;
import org.sonatype.plugin.metadata.GAVCoordinate;
import org.sonatype.plugin.metadata.PluginMetadataGenerationRequest;
import org.sonatype.plugin.metadata.PluginMetadataGenerator;
import org.sonatype.plugin.metadata.gleaner.GleanerException;
import com.redhat.tools.nexus.maven.plugin.ClasspathUtils;
import com.redhat.tools.nexus.maven.plugin.NexusApplicationInformation;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Generates a plugin's <tt>plugin.xml</tt> descriptor file based on the project's pom and class annotations.
*
* @goal generate-metadata
* @phase process-classes
* @requiresDependencyResolution test
*/
public class PluginDescriptorMojo
extends AbstractMojo
{
/**
* Instructions for selecting dependency artifacts to be bundles, vs. those to be excluded
* (as in cases where they're shaded into the main artifact).
*
* @parameter
*/
private ArtifactSet artifactSet;
/**
* A list of groupId:artifactId references to non-plugin dependencies that contain components which should be
* gleaned for this plugin build.
*
* @parameter
*/
private List<String> componentDependencies;
/**
* The output location for the generated plugin descriptor. <br/>
* <b>NOTE:</b> Default value for this field is supplied by the {@link ApplicationInformation} component included
* via build extension.
*
* @parameter
*/
private File generatedPluginMetadata;
/**
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject mavenProject;
/**
* The ID of the target application. For example if this plugin was for the Nexus Repository Manager, the ID would
* be, 'nexus'. <br/>
* <b>NOTE:</b> Default value for this field is supplied by the {@link ApplicationInformation} component included
* via build extension.
*
* @parameter
*/
private String applicationId;
/**
* The edition of the target application. Some applications come in multiple flavors, OSS, PRO, Free, light, etc. <br/>
* <b>NOTE:</b> Default value for this field is supplied by the {@link ApplicationInformation} component included
* via build extension.
*
* @parameter expression="OSS"
*/
private String applicationEdition;
/**
* The minimum product version of the target application. <br/>
* <b>NOTE:</b> Default value for this field is supplied by the {@link ApplicationInformation} component included
* via build extension.
*
* @parameter
*/
private String applicationMinVersion;
/**
* The maximum product version of the target application. <br/>
* <b>NOTE:</b> Default value for this field is supplied by the {@link ApplicationInformation} component included
* via build extension, if it specified at all.
*
* @parameter
*/
private String applicationMaxVersion;
/** @component */
private PluginMetadataGenerator metadataGenerator;
private static final NexusApplicationInformation mapping = new NexusApplicationInformation();
/**
* @parameter expression="${project.scm.developerConnection}"
* @readonly
*/
private String urlScm;
/**
* The username that is used when connecting to the SCM system.
*
* @parameter expression="${username}"
* @since 1.0-beta-1
*/
private String username;
/**
* The password that is used when connecting to the SCM system.
*
* @parameter expression="${password}"
* @since 1.0-beta-1
*/
private String password;
/**
* @component
*/
private ScmManager scmManager;
@SuppressWarnings( "unchecked" )
public void execute()
throws MojoExecutionException, MojoFailureException
{
if ( !mavenProject.getPackaging().equals( mapping.getPluginPackaging() ) )
{
getLog().info( "Project is not of packaging type '" + mapping.getPluginPackaging() + "'." );
return;
}
initConfig();
final PluginMetadataGenerationRequest request = new PluginMetadataGenerationRequest();
request.setGroupId( mavenProject.getGroupId() );
request.setArtifactId( mavenProject.getArtifactId() );
request.setVersion( mavenProject.getVersion() );
request.setName( mavenProject.getName() );
request.setDescription( mavenProject.getDescription() );
request.setPluginSiteURL( mavenProject.getUrl() );
request.setApplicationId( applicationId );
request.setApplicationEdition( applicationEdition );
request.setApplicationMinVersion( applicationMinVersion );
request.setApplicationMaxVersion( applicationMaxVersion );
// licenses
if ( mavenProject.getLicenses() != null )
{
for ( final License mavenLicenseModel : (List<License>) mavenProject.getLicenses() )
{
request.addLicense( mavenLicenseModel.getName(), mavenLicenseModel.getUrl() );
}
}
// scm information
try
{
final ScmRepository repository = getScmRepository();
final SvnInfoScmResult scmResult = scmInfo( repository, new ScmFileSet( mavenProject.getBasedir() ) );
if ( !scmResult.isSuccess() )
{
throw new ScmException( scmResult.getCommandOutput() );
}
final SvnInfoItem info = (SvnInfoItem) scmResult.getInfoItems().get( 0 );
request.setScmVersion( info.getLastChangedRevision() );
request.setScmTimestamp( info.getLastChangedDate() );
}
catch ( final ScmException e )
{
getLog().warn( "Failed to get scm information: " + e.getMessage() );
}
// dependencies
final List<Artifact> artifacts = mavenProject.getTestArtifacts();
final Set<Artifact> classpathArtifacts = new HashSet<Artifact>();
if ( artifacts != null )
{
final Set<String> excludedArtifactIds = new HashSet<String>();
final ArtifactSelector selector =
artifactSet == null ? null : new ArtifactSelector( mavenProject.getArtifact(), artifactSet );
artifactLoop: for ( final Artifact artifact : artifacts )
{
final GAVCoordinate artifactCoordinate =
new GAVCoordinate( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
artifact.getClassifier(), artifact.getType() );
if ( artifact.getType().equals( mapping.getPluginPackaging() ) )
{
if ( artifact.isSnapshot() )
{
artifactCoordinate.setVersion( artifact.getBaseVersion() );
}
if ( !Artifact.SCOPE_PROVIDED.equals( artifact.getScope() ) )
{
throw new MojoFailureException( "Plugin dependency \"" + artifact.getDependencyConflictId()
+ "\" must have the \"provided\" scope!" );
}
excludedArtifactIds.add( artifact.getId() );
request.addPluginDependency( artifactCoordinate );
}
else if ( Artifact.SCOPE_PROVIDED.equals( artifact.getScope() )
|| Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
{
excludedArtifactIds.add( artifact.getId() );
}
else if ( ( Artifact.SCOPE_COMPILE.equals( artifact.getScope() ) || Artifact.SCOPE_RUNTIME.equals( artifact.getScope() ) )
&& ( !mapping.matchesCoreGroupIds( artifact.getGroupId() ) ) )
{
if ( artifact.getDependencyTrail() != null )
{
for ( final String trailId : (List<String>) artifact.getDependencyTrail() )
{
if ( excludedArtifactIds.contains( trailId ) )
{
getLog().debug(
"Dependency artifact: "
+ artifact.getId()
+ " is part of the transitive dependency set for a dependency with 'provided' or 'test' scope: "
+ trailId
+ "\nThis artifact will be excluded from the plugin classpath." );
continue artifactLoop;
}
}
}
if ( selector == null || selector.isSelected( artifact ) )
{
if ( componentDependencies != null
&& componentDependencies.contains( artifact.getGroupId() + ":" + artifact.getArtifactId() ) )
{
artifactCoordinate.setHasComponents( true );
}
request.addClasspathDependency( artifactCoordinate );
classpathArtifacts.add( artifact );
}
else
{
getLog().debug( "Excluding: " + artifact.getId() + "; excluded by artifactSet." );
excludedArtifactIds.add( artifact.getId() );
}
}
}
}
request.setOutputFile( generatedPluginMetadata );
request.setClassesDirectory( new File( mavenProject.getBuild().getOutputDirectory() ) );
try
{
if ( mavenProject.getCompileClasspathElements() != null )
{
for ( final String classpathElement : (List<String>) mavenProject.getCompileClasspathElements() )
{
request.getClasspath().add( new File( classpathElement ) );
}
}
}
catch ( final DependencyResolutionRequiredException e )
{
throw new MojoFailureException( "Plugin failed to resolve dependencies: " + e.getMessage(), e );
}
request.getAnnotationClasses().add( ExtensionPoint.class );
request.getAnnotationClasses().add( Managed.class );
// do the work
try
{
metadataGenerator.generatePluginDescriptor( request );
}
catch ( final GleanerException e )
{
throw new MojoFailureException( "Failed to generate plugin xml file: " + e.getMessage(), e );
}
try
{
ClasspathUtils.write( classpathArtifacts, mavenProject );
}
catch ( final IOException e )
{
throw new MojoFailureException( "Failed to generate classpath properties file: " + e.getMessage(), e );
}
}
private void initConfig()
throws MojoFailureException
{
if ( generatedPluginMetadata == null )
{
try
{
generatedPluginMetadata = mapping.getPluginMetadataFile( mavenProject );
}
catch ( final InterpolationException e )
{
throw new MojoFailureException( "Cannot calculate plugin metadata file location from expression: "
+ mapping.getPluginMetadataPath(), e );
}
}
applicationId = applicationId == null ? mapping.getApplicationId() : applicationId;
applicationEdition = applicationEdition == null ? mapping.getApplicationEdition() : applicationEdition;
applicationMinVersion =
applicationMinVersion == null ? mapping.getApplicationMinVersion() : applicationMinVersion;
applicationMaxVersion =
applicationMaxVersion == null ? mapping.getApplicationMaxVersion() : applicationMaxVersion;
}
private ScmRepository getScmRepository()
throws ScmException
{
if ( StringUtils.isEmpty( urlScm ) )
{
throw new ScmException( "No SCM URL found." );
}
ScmRepository repository;
repository = scmManager.makeScmRepository( urlScm );
final ScmProviderRepository scmRepo = repository.getProviderRepository();
if ( !StringUtils.isEmpty( username ) )
{
scmRepo.setUser( username );
}
if ( !StringUtils.isEmpty( password ) )
{
scmRepo.setPassword( password );
}
return repository;
}
public SvnInfoScmResult scmInfo( final ScmRepository repository, final ScmFileSet fileSet )
throws ScmException
{
final AbstractSvnScmProvider abstractSvnScmProvider =
(AbstractSvnScmProvider) scmManager.getProviderByType( "svn" );
return abstractSvnScmProvider.info( repository.getProviderRepository(), fileSet, null );
}
}